#include <atomic>
#include <chrono>
#include <condition_variable>
#include <functional>
#include <iostream>
#include <memory>
#include <mutex>
#include <queue>
#include <semaphore>
#include <thread>
#include <vector>
using ThreadFunc = std::function<void()>;
class Task {
 public:
  virtual void Run() = 0;
};
class Thread {
 public:
  Thread(ThreadFunc func) : func_(std::move(func)) {}
  ~Thread() {}
  void Start();

 private:
  ThreadFunc func_;
  std::unique_ptr<std::thread> thread_;
};
class ThreadPool {
 public:
  ThreadPool(int task_que_max_num = 256) : task_que_max_num_(task_que_max_num) {
    task_num_ = 0;
  }
  ~ThreadPool() {}
  ThreadPool(const ThreadPool&) = delete;
  ThreadPool operator=(const ThreadPool&) = delete;

  void Start();
  void SetThreadNum(int thread_init_num = -1);
  void AddTask(std::shared_ptr<Task> sp);

 private:
  void ThreadRunFunc();

 private:
  std::vector<std::unique_ptr<Thread>> thread_vec_;
  int thread_init_num_;
  ThreadFunc func_;

  std::queue<std::shared_ptr<Task>> task_que_;
  std::atomic_int task_num_;
  int task_que_max_num_;

  std::mutex mtx_;
  std::condition_variable producer_cond_;
  std::condition_variable consumer_cond_;
};

void Thread::Start() {
  thread_ = std::make_unique<std::thread>(func_);
  thread_->detach();
}
// 用户向线程池提交任务,生产！
void ThreadPool::AddTask(std::shared_ptr<Task> sp) {
  std::unique_lock<std::mutex> ul(mtx_);
  if (!producer_cond_.wait_for(ul, std::chrono::seconds(1), [&]() -> bool {
        return task_num_ < task_que_max_num_;
      }))
    std::cerr << "task_que is full, AddTask failed!" << std::endl;
  task_que_.emplace(sp);  // task_que_.emplace(std::move(sp));
  task_num_++;
  std::cout << "AddTask successfully!" << std::endl;
  consumer_cond_.notify_one();  // FIXME:是否这行代码不用锁住？
  // consumer_cond_.notify_all();
}
// 具体的一个任务执行流程，消费！
void ThreadPool::ThreadRunFunc() {
  while (1) {
    std::shared_ptr<Task> task;
    {
      std::unique_lock<std::mutex> ul(mtx_);
      consumer_cond_.wait(ul, [&]() -> bool { return task_num_ > 0; });
      task = task_que_.front();
      task_que_.pop();
      task_num_--;
      if (task_num_ > 0) consumer_cond_.notify_one();
    }

    if (task != nullptr) task->Run();
  }
}
void ThreadPool::Start() {
  for (int i = 0; i < thread_init_num_; i++) {
    thread_vec_.emplace_back(std::move(
        std::make_unique<Thread>(std::bind(&ThreadPool::ThreadRunFunc, this))));
  }
  for (const auto& t : thread_vec_) t->Start();
}

void ThreadPool::SetThreadNum(int thread_init_num) {
  if (thread_init_num == -1)
    thread_init_num_ =
        std::thread::hardware_concurrency();  // TODO:1807496520 bug?
  else
    thread_init_num_ = thread_init_num;
}